package com.googlecode.esblabs.util.test;

import org.mule.api.MuleEventContext;
import org.mule.api.MuleException;
import org.mule.component.DefaultJavaComponent;
import org.mule.module.client.MuleClient;
import org.mule.tck.FunctionalTestCase;
import org.mule.tck.functional.EventCallback;
import org.mule.tck.functional.FunctionalTestComponent;

/**
 * Makes asynchronous functional tests easier. Assumes that the payload can be converted to a String for comparation with a String based expected result.
 * Sample usage:
 * 
 * <tt>
 * public void testS2FireAndForgetJms() throws Exception {
 *		String      endpoint       = "vm://S2";
 *		SampleValue payload        = new SampleValue(1, "Row one");
 *		String      expectedResult = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><sampleValue><id>1</id><value>Row one</value></sampleValue>";
 *		String      mockComp       = "S2-FireAndForget-Jms-MOCK";
 *
 *		testAsynchronousService(endpoint, payload, expectedResult, mockComp);
 * }
 * </tt>
 * @author magnus larsson
 */
public abstract class AbstractAsynchronousTestCase extends FunctionalTestCase {

	private class ValueHolder {
		public Object value;
		public ValueHolder(Object value) {
			this.value = value;
		}
	}
	
	private MuleClient c = null;

	private MuleClient getMuleClient() {
		try {
			if (c == null) c = new MuleClient();
			return c;
		} catch (MuleException e) {
			throw new RuntimeException(e);
		}
	}

	protected void testAsynchronousService(String endpoint, Object payload, String expectedResult, String mockComp)
			throws Exception, MuleException, InterruptedException {

		ValueHolder gotCallback = new ValueHolder(false);

		EventCallback callback = new AssertCallback(expectedResult, gotCallback);

	    registerCallback(mockComp, callback);

		MuleClient c = getMuleClient();
	    
		// Use dispatch() and Thread.sleep() to avoind problems with temp reply-to queues that mule create by defualt when using send()
		c.dispatch(endpoint, payload, null);
		Thread.sleep(2000);
		
		assertTrue("Did not revecive a asynchronous callback", ((Boolean)gotCallback.value).booleanValue());
	}

	private void registerCallback(final String mockComp, EventCallback callback) throws Exception {
		DefaultJavaComponent defaultComponent = (DefaultJavaComponent)muleContext.getRegistry().lookupService(mockComp).getComponent();
	    FunctionalTestComponent testComponent = (FunctionalTestComponent)defaultComponent.getObjectFactory().getInstance();
	    testComponent.setEventCallback(callback);
	}
	
	private class AssertCallback implements EventCallback  {

		private String      expectedResult = null;
		private ValueHolder gotCallback    = null;

		public AssertCallback(String expectedResult, ValueHolder gotCallback) {
			this.expectedResult = expectedResult;
			this.gotCallback    = gotCallback;
		}
		
        public void eventReceived(MuleEventContext context, Object component) throws Exception {
        	gotCallback.value = true;
    		String result = context.getMessage().getPayloadAsString();
    		logger.info("AssertCallback: " + result);
    		assertEquals(expectedResult, result);	      	    	
        }
    };
	
}
